/*
 *  Copyright (c) M2TK Project. All rights reserved.
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */
package m2tk.assistant.app.ui.component;

import cn.hutool.core.util.StrUtil;
import com.formdev.flatlaf.FlatClientProperties;
import com.formdev.flatlaf.icons.FlatSearchIcon;
import m2tk.assistant.app.ui.model.StreamInfoTableModel;
import m2tk.assistant.app.ui.util.ComponentUtil;
import m2tk.assistant.app.ui.util.ThreeStateRowSorterListener;
import m2tk.assistant.common.domain.ElementaryStream;
import m2tk.assistant.common.presets.StreamTypes;
import m2tk.assistant.common.ui.RoundPanel;
import net.miginfocom.swing.MigLayout;
import org.kordamp.ikonli.fluentui.FluentUiFilledAL;
import org.kordamp.ikonli.fluentui.FluentUiFilledMZ;
import org.kordamp.ikonli.swing.FontIcon;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.swing.*;
import javax.swing.table.DefaultTableCellRenderer;
import javax.swing.table.TableCellRenderer;
import javax.swing.table.TableColumnModel;
import javax.swing.table.TableRowSorter;
import java.awt.Color;
import java.awt.Component;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.List;
import java.util.function.BiConsumer;

import static m2tk.assistant.common.ui.UIConstants.*;

public class ESSummaryPane extends RoundPanel
{
    private static final Logger log = LoggerFactory.getLogger(ESSummaryPane.class);

    private StreamInfoTableModel model;
    private BiConsumer<MouseEvent, ElementaryStream> popupListener;

    public ESSummaryPane()
    {
        initUI();
    }

    private void initUI()
    {
        model = new StreamInfoTableModel();
        TableRowSorter<StreamInfoTableModel> rowSorter = new TableRowSorter<>(model);
        rowSorter.addRowSorterListener(new ThreeStateRowSorterListener(rowSorter));

        JTextField fieldSearch = new JTextField();
        fieldSearch.putClientProperty(FlatClientProperties.PLACEHOLDER_TEXT, "输入PID或基本流描述中的关键字进行过滤……");
        fieldSearch.putClientProperty(FlatClientProperties.TEXT_FIELD_LEADING_ICON, new FlatSearchIcon());
        fieldSearch.putClientProperty(FlatClientProperties.TEXT_FIELD_SHOW_CLEAR_BUTTON, true);
        fieldSearch.putClientProperty(FlatClientProperties.TEXT_FIELD_CLEAR_CALLBACK, (Runnable) () ->
        {
            fieldSearch.setText("");
            rowSorter.setRowFilter(null);
        });
        Runnable doFiltering = () ->
        {
            String keyword = fieldSearch.getText();
            rowSorter.setRowFilter(StrUtil.isNotEmpty(keyword)
                                   ? RowFilter.regexFilter("(?i)" + keyword, 1, 2) // 忽略大小写搜索
                                   : null);
        };
        fieldSearch.addActionListener(e -> doFiltering.run());
        fieldSearch.addKeyListener(new KeyAdapter()
        {
            @Override
            public void keyReleased(KeyEvent e)
            {
                if (!e.isActionKey())
                    doFiltering.run();
            }
        });

        JTable table = createTable();
        table.setModel(model);
        table.setRowSorter(rowSorter);
        table.setRowSelectionAllowed(true);
        table.getTableHeader().setReorderingAllowed(false);
        table.setAutoResizeMode(JTable.AUTO_RESIZE_ALL_COLUMNS);
        table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
        table.addMouseListener(new MouseAdapter()
        {
            @Override
            public void mouseReleased(MouseEvent e)
            {
                if (e.isPopupTrigger() && popupListener != null)
                {
                    int selectedRow = table.getSelectedRow();
                    int rowAtPoint = table.rowAtPoint(e.getPoint());

                    if (rowAtPoint == -1)
                        return;
                    if (rowAtPoint != selectedRow)
                    {
                        table.setRowSelectionInterval(table.convertRowIndexToModel(rowAtPoint),
                                                      table.convertRowIndexToModel(rowAtPoint));
                        selectedRow = rowAtPoint;
                    }
                    try
                    {
                        popupListener.accept(e, model.getRow(table.convertRowIndexToModel(selectedRow)));
                    } catch (Exception ignored)
                    {
                    }
                }
            }
        });

        ToolTipManager.sharedInstance().registerComponent(table);

        DefaultTableCellRenderer leadingRenderer = new DefaultTableCellRenderer();
        leadingRenderer.setHorizontalAlignment(SwingConstants.LEADING);
        StreamRatioCellRenderer ratioRenderer = new StreamRatioCellRenderer();
        StreamStateCellRenderer stateRenderer = new StreamStateCellRenderer();

        // "序号", "PID", "基本流描述", "带宽占比", "传输错误", "连续计数错误", "流状态"
        TableColumnModel columnModel = table.getColumnModel();
        ComponentUtil.configTableColumn(columnModel, 0, leadingRenderer, 60, false);
        ComponentUtil.configTableColumn(columnModel, 1, leadingRenderer, 150, false);
        ComponentUtil.configTableColumn(columnModel, 2, leadingRenderer, 500, true);
        ComponentUtil.configTableColumn(columnModel, 3, ratioRenderer, 150, false);
        ComponentUtil.configTableColumn(columnModel, 4, leadingRenderer, 100, false);
        ComponentUtil.configTableColumn(columnModel, 5, leadingRenderer, 100, false);
        ComponentUtil.configTableColumn(columnModel, 6, leadingRenderer, 120, false);
        ComponentUtil.configTableColumn(columnModel, 7, stateRenderer, 200, true);

        setLayout(new MigLayout("insets 10", "[][grow][]", "[25!][pref!][][grow]"));
        add(new JLabel("基本流概览",
                       StreamInfoNaviPane.BOARD_ICON,
                       SwingConstants.TRAILING), "left");

        add(fieldSearch, "gap 30, left, grow, wrap");
        add(separator(), "span 3, grow, wrap");
        add(createScrollPane(table), "span 3 2, grow");
    }

    public void setPopupListener(BiConsumer<MouseEvent, ElementaryStream> popupListener)
    {
        this.popupListener = popupListener;
    }

    public void updateElementaryStreamSummary(List<ElementaryStream> streams)
    {
        model.update(streams);
    }

    private static class StreamRatioCellRenderer extends JPanel implements TableCellRenderer
    {
        private final JProgressBar progressBar;

        StreamRatioCellRenderer()
        {
            progressBar = new JProgressBar(0, 100);
            progressBar.setStringPainted(true);
            setLayout(new MigLayout("ins 3, fill"));
            add(progressBar, "grow");
        }

        @Override
        public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column)
        {
            if (table == null)
            {
                return this;
            }

            if (isSelected)
            {
                setBackground(table.getSelectionBackground());
            } else
            {
                Color background = table.getBackground();
                if (background == null || background instanceof javax.swing.plaf.UIResource)
                {
                    Color alternateColor = UIManager.getColor("Table.alternateRowColor");
                    if (alternateColor != null && row % 2 != 0)
                    {
                        background = alternateColor;
                    }
                }
                setBackground(background);
            }

            if (hasFocus && !isSelected && table.isCellEditable(row, column))
            {
                Color col = UIManager.getColor("Table.focusCellBackground");
                if (col != null)
                    setBackground(col);
            }

            setValue(value);

            return this;
        }

        private void setValue(Object value)
        {
            try
            {
                Double ratio = (Double) value;
                progressBar.setValue((int) (100 * ratio));
                setToolTipText(String.format("%.2f", ratio));
            } catch (Exception ex)
            {
                log.error("转译带宽占比表示时异常：{}, ex：{}", value, ex.getMessage());
            }
        }
    }

    private static class StreamStateCellRenderer extends JPanel implements TableCellRenderer
    {
        private final JLabel labelDataP;
        private final JLabel labelDataS;
        private final JLabel labelVideoP;
        private final JLabel labelVideoS;
        private final JLabel labelAudioP;
        private final JLabel labelAudioS;
        private final JLabel labelPrivate;
        private final JLabel labelPCR;
        private final JLabel labelTSE;
        private final JLabel labelCCE;

        StreamStateCellRenderer()
        {
            labelDataP = new JLabel(FontIcon.of(FluentUiFilledMZ.TABLE_24, ICON_SIZE_S, GREEN));
            labelDataS = new JLabel(FontIcon.of(FluentUiFilledMZ.TABLE_24, ICON_SIZE_S, RED));
            labelVideoP = new JLabel(FontIcon.of(FluentUiFilledMZ.VIDEO_24, ICON_SIZE_S, GREEN));
            labelVideoS = new JLabel(FontIcon.of(FluentUiFilledMZ.VIDEO_24, ICON_SIZE_S, RED));
            labelAudioP = new JLabel(FontIcon.of(FluentUiFilledMZ.SPEAKER_24, ICON_SIZE_S, GREEN));
            labelAudioS = new JLabel(FontIcon.of(FluentUiFilledMZ.SPEAKER_24, ICON_SIZE_S, RED));
            labelPrivate = new JLabel(FontIcon.of(FluentUiFilledMZ.SLIDE_TEXT_24, ICON_SIZE_S, LIGHT_BLUE));
            labelPCR = new JLabel(FontIcon.of(FluentUiFilledAL.CLOCK_24, ICON_SIZE_S, BRIGHT_BLUE));
            labelTSE = new JLabel(FontIcon.of(FluentUiFilledMZ.WARNING_24, ICON_SIZE_S, ORANGE));
            labelCCE = new JLabel(FontIcon.of(FluentUiFilledAL.CLOSED_CAPTION_24, ICON_SIZE_S, ORANGE));

            setLayout(new MigLayout("ins 2 10 2 10, hidemode 3"));
            add(labelDataP, "cell 0 0");
            add(labelDataS, "cell 0 0");
            add(labelVideoP, "cell 0 0");
            add(labelVideoS, "cell 0 0");
            add(labelAudioP, "cell 0 0");
            add(labelAudioS, "cell 0 0");
            add(labelPrivate, "cell 0 0");
            add(labelPCR);
            add(labelTSE);
            add(labelCCE);
            add(Box.createHorizontalGlue(), "grow");
        }

        @Override
        public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column)
        {
            if (table == null)
                return this;

            if (isSelected)
            {
                setBackground(table.getSelectionBackground());
            } else
            {
                Color background = table.getBackground();
                if (background == null || background instanceof javax.swing.plaf.UIResource)
                {
                    Color alternateColor = UIManager.getColor("Table.alternateRowColor");
                    if (alternateColor != null && row % 2 != 0)
                    {
                        background = alternateColor;
                    }
                }
                setBackground(background);
            }

            if (hasFocus && !isSelected && table.isCellEditable(row, column))
            {
                Color col = UIManager.getColor("Table.focusCellBackground");
                if (col != null)
                    setBackground(col);
            }

            setValue(value);

            return this;
        }

        private void setValue(Object value)
        {
            try
            {
                String s = (String) value;
                String[] states = s.split(",");

                labelDataP.setVisible(StrUtil.equals(states[0], StreamTypes.CATEGORY_DATA) && !Boolean.parseBoolean(states[1]));
                labelDataS.setVisible(StrUtil.equals(states[0], StreamTypes.CATEGORY_DATA) && Boolean.parseBoolean(states[1]));
                labelVideoP.setVisible(StrUtil.equals(states[0], StreamTypes.CATEGORY_VIDEO) && !Boolean.parseBoolean(states[1]));
                labelVideoS.setVisible(StrUtil.equals(states[0], StreamTypes.CATEGORY_VIDEO) && Boolean.parseBoolean(states[1]));
                labelAudioP.setVisible(StrUtil.equals(states[0], StreamTypes.CATEGORY_AUDIO) && !Boolean.parseBoolean(states[1]));
                labelAudioS.setVisible(StrUtil.equals(states[0], StreamTypes.CATEGORY_AUDIO) && Boolean.parseBoolean(states[1]));
                labelPrivate.setVisible(StrUtil.equals(states[0], StreamTypes.CATEGORY_USER_PRIVATE));
                labelPCR.setVisible(Boolean.parseBoolean(states[2]));
                labelTSE.setVisible(Boolean.parseBoolean(states[3]));
                labelCCE.setVisible(Boolean.parseBoolean(states[4]));

                String category = switch (states[0])
                {
                    case StreamTypes.CATEGORY_DATA -> "数据";
                    case StreamTypes.CATEGORY_VIDEO -> "视频";
                    case StreamTypes.CATEGORY_AUDIO -> "音频";
                    case StreamTypes.CATEGORY_USER_PRIVATE -> "用户私有格式";
                    default -> "未知";
                };
                setToolTipText(String.format("""
                                             流类型：%s
                                             携带PCR：%s
                                             加扰：%s
                                             """,
                                             category,
                                             Boolean.parseBoolean(states[2]) ? "是" : "否",
                                             Boolean.parseBoolean(states[1]) ? "是" : "否"));
            } catch (Exception ex)
            {
                log.error("转译流状态表示时异常：{}", value);
            }
        }
    }
}
